//
// Created by Administrator on 2015/5/24.
//

#include "es_log.h"
#include "es_socket.h"

#ifdef __ESVR_LINUX__
#include <sys/select.h>
#define MAX_FD1(a, b) (( a > b ? a : b) + 1)
#else
#include <winsock.h>
#define MAX_FD1(a, b) (0)
#endif
#include <set>

namespace esvr
{
    void IOManager::main_loop()
    {
        m_io_type = "select";
        fd_set fdset, allset;
        FD_ZERO(&allset);
        FD_SET(m_serverfd, &allset);
        struct timeval tv;
        socket_t  max_fd1= MAX_FD1(m_serverfd, 0);
        while(m_start)
        {
            auto queue = get_to_close_queue();
            socket_t* pfd;
            while((pfd = queue->pop()) != NULL)
            {
                FD_CLR(*pfd, &allset);
                es_close(*pfd);
                delete_fd(*pfd);
                log_info("force close client: function:%s , line:%d, errno:%d", __FUNCTION__, __LINE__, ERRNO);
                //event dispatch
                ClosedEvent event{*pfd};
                EventBus::get_instance().fire_event(&event);
            }
            fdset = allset;
            tv.tv_sec = m_wait_millisecond / 1000;
            tv.tv_usec = (m_wait_millisecond % 1000) * 1000;
            before_loop_once();
            int ret = ::select(max_fd1, &fdset, NULL, NULL, &tv);
            after_loop_once();
            if(ret < 0)
            {
                log_fatal("error select in function:%s, line:%d, ret:%d, errno:%d", __FUNCTION__, __LINE__, ret, ERRNO);
                break;
            }
            else if(ret == 0)
            {
                on_idle();
            }
            else
            {
                std::set<socket_t> to_remove;
                for(auto& it : m_active_clients)
                {
                    if(FD_ISSET(it.first, &fdset))
                    {
                        //LOG_DEBUG("client has data:%d", it);
                        char *buffer = NULL;
                        size_t buffer_size = 0;
                        writable_buffer(it.first, buffer, buffer_size);
                        if(buffer == NULL || buffer_size <= 0){
                          LOG_ERROR("writable buffer for socket ", it.first, " is null!");
                          continue;
                        }
                        int len = es_recv(it.first, buffer, buffer_size);
                        if(len <= 0)
                        {
                            FD_CLR(it.first, &allset);
                            es_close(it.first);
                            to_remove.insert(it.first);
                            LOG_ERROR("recv error: function:%s , line:%d, ret:%d, errno:%d", __FUNCTION__, __LINE__, len, ERRNO);
                        }
                        else
                        {
                            increase_readable(it.first, static_cast<size_t>(len));
                            ReadEvent event{it.first, buffer, static_cast<size_t>(len)};
                            EventBus::get_instance().fire_event(&event);
                        }
                    }
                }
                if(to_remove.size() > 0)
                {
                    for(auto& it : to_remove)
                    {
                        delete_fd(it);
                        ClosedEvent event{it};
                        EventBus::get_instance().fire_event(&event);
                    }
                }
                if(FD_ISSET(m_serverfd, &fdset))
                {
                    socket_t client_fd = es_accept(m_serverfd);
                    if(client_fd > 0)
                    {
                        LOG_DEBUG("new client accept:%d", client_fd);
                        max_fd1 = MAX_FD1(max_fd1 -1, client_fd);
                        FD_SET(client_fd, &allset);
                        new_fd(client_fd);
                        ConnectedEvent event{client_fd};
                        EventBus::get_instance().fire_event(&event);
                    }
                }
            }
        }
    }
};
